using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
using System.Linq.Expressions;

namespace Polymod
{
    public static class ModelExtensions
    {
        //TODO: Create a function for enumerables of all primitive types!!!
        public static IModelCollection<R> GetCollection<T, R>(this IModel<T> model, Expression<Func<T, IEnumerable<R>>> property)
        {
            //NOTE: Assume here that R is NOT a primitive, should have one extension for every primitive that matches...
            //TODO: Enum is passed in, but ICollection is returned. Make sure that one can be converted to the other in the Template Builder.

            return (IModelCollection<R>)model.Get(GetPropertyName(property));
        }
        //..like this, need one for each primitive type to fit in the enumarable...
        public static ICollection<int> GetCollection<T>(this IModel<T> model, Expression<Func<T, IEnumerable<int>>> property)
        {
            return (ICollection<int>)model.Get(GetPropertyName(property));
        }


        //TODO: Do this for ALL primitive types (include DateTime?, Include Nullables also!).
        public static int Get<T>(this IModel<T> model, Expression<Func<T, int>> property)
        {
            return (int)model.Get(GetPropertyName(property));
        }
        public static string Get<T>(this IModel<T> model, Expression<Func<T, string>> property)
        {
            return (string)model.Get(GetPropertyName(property));
        }

        public static IModel<R> Get<T, R>(this IModel<T> model, Expression<Func<T, R>> property)
        {
            return (IModel<R>)model.Get(GetPropertyName(property));
        }

        public static void Set<T, R>(this IModel<T> model, Expression<Func<T, R>> property, R value)
        {
            model.Set(GetPropertyName(property), value);
        }

        private static string GetPropertyName<T, R>(Expression<Func<T, R>> expression)
        {
            if (expression == null) throw new System.ArgumentNullException("expression");
            var visitor = new PropertyNameVisitor();
            visitor.Visit(expression);
            if (visitor.FieldOrPropertyName == null) throw new InvalidOperationException(string.Format("Invalid expression. {0}", expression));
            return visitor.FieldOrPropertyName;
        }

        private class PropertyNameVisitor : ExpressionVisitor
        {
            private string _fieldOrPropertyName;

            /// <summary>
            /// Gets FieldOrPropertyName
            /// </summary>
            public string FieldOrPropertyName
            {
                get { return _fieldOrPropertyName; }
            }

            protected override Expression VisitMember(MemberExpression node)
            {
                if (node.Member.MemberType == MemberTypes.Property || node.Member.MemberType == MemberTypes.Field)
                {
                    _fieldOrPropertyName = node.Member.Name;
                    //Do not recursively visit (found what we were looking for already), just return the node.
                    return node;
                }
                return base.VisitMember(node);
            }
        }

    }
}
